package cfsvn.export;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.FilterOutputStream;
import java.util.zip.ZipOutputStream;
import java.util.zip.ZipEntry;

import org.tmatesoft.svn.core.SVNCommitInfo;
import org.tmatesoft.svn.core.SVNErrorCode;
import org.tmatesoft.svn.core.SVNErrorMessage;
import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.SVNNodeKind;
import org.tmatesoft.svn.core.SVNURL;
import org.tmatesoft.svn.core.auth.ISVNAuthenticationManager;
import org.tmatesoft.svn.core.internal.io.dav.DAVRepositoryFactory;
import org.tmatesoft.svn.core.internal.io.fs.FSRepositoryFactory;
import org.tmatesoft.svn.core.internal.io.svn.SVNRepositoryFactoryImpl;
import org.tmatesoft.svn.core.io.ISVNEditor;
import org.tmatesoft.svn.core.io.ISVNReporter;
import org.tmatesoft.svn.core.io.ISVNReporterBaton;
import org.tmatesoft.svn.core.io.SVNRepository;
import org.tmatesoft.svn.core.io.SVNRepositoryFactory;
import org.tmatesoft.svn.core.io.diff.SVNDeltaProcessor;
import org.tmatesoft.svn.core.io.diff.SVNDiffWindow;
import org.tmatesoft.svn.core.wc.SVNWCUtil;

public class ZipExport {
	
	public ZipExport() {	
	}
	
	public class ExportReporterBaton implements ISVNReporterBaton {

		private long exportRevision;

		public ExportReporterBaton(long revision) {
			exportRevision = revision;
		}

		public void report(ISVNReporter reporter) throws SVNException {
			try {
				reporter.setPath("", null, exportRevision, true);
				reporter.finishReport();
			} catch (SVNException svne) {
				reporter.abortReport();
				System.out.println("Report failed");
			}
		}
	}

	public class ExportEditor implements ISVNEditor {

		private File myZipFile;
		private SVNDeltaProcessor myDeltaProcessor;
		private ZipOutputStream myZipOut;

		public ExportEditor(File zipFile) {
			myZipFile = zipFile;
			myDeltaProcessor = new SVNDeltaProcessor();
		}

		public void targetRevision(long revision) throws SVNException {
		}

		public void openRoot(long revision) throws SVNException {
			try {
				myZipOut = new ZipOutputStream(new FileOutputStream(myZipFile));
			} catch (IOException ioe) {
				ioe.printStackTrace();
			}
		}

		public void addDir(String path, String copyFromPath, long copyFromRevision) throws SVNException {
			//        try{
			//        myZipOut.putNextEntry(new ZipEntry(path));
			//        }
			//        catch(IOException ioe){
			//    ioe.printStackTrace();
			//    }
		}

		public void openDir(String path, long revision) throws SVNException {
		}

		public void changeDirProperty(String name, String value) throws SVNException {
		}

		public void addFile(String path, String copyFromPath, long copyFromRevision) throws SVNException {
 		try {
				myZipOut.putNextEntry(new ZipEntry(path));
			} catch (IOException ioe) {
				ioe.printStackTrace();
			}	
		}

		public void openFile(String path, long revision) throws SVNException {
		}

		public void changeFileProperty(String path, String name, String value) throws SVNException {
		}

		public void applyTextDelta(String path, String baseChecksum) throws SVNException {
			OutputStream stream = new FilterOutputStream(myZipOut) {
				public void write(byte b[], int off, int len) throws IOException {
					myZipOut.write(b, off, len);
				}

				public void close() {
					// do nothing here.
				}
			};

			myDeltaProcessor.applyTextDelta(null, stream, false);
		}

		public OutputStream textDeltaChunk(String path, SVNDiffWindow diffWindow) throws SVNException {
			return myDeltaProcessor.textDeltaChunk(diffWindow);
		}

		public void textDeltaEnd(String path) throws SVNException {
			myDeltaProcessor.textDeltaEnd();
		}

		public void closeFile(String path, String textChecksum) throws SVNException {
			try {
				myZipOut.closeEntry();
			} catch (IOException ioe) {
				ioe.printStackTrace();
			}
		}

		public void closeDir() throws SVNException {
			try {
				myZipOut.closeEntry();
			} catch (IOException ioe) {
				ioe.printStackTrace();
			}
		}

		public void deleteEntry(String path, long revision) throws SVNException {
		}

		public void absentDir(String path) throws SVNException {
		}

		public void absentFile(String path) throws SVNException {
		}

		public SVNCommitInfo closeEdit() throws SVNException {
			try {
				myZipOut.close();
			} catch (IOException ioe) {
				ioe.printStackTrace();
			}
			return null;
		}

		public void abortEdit() throws SVNException {
		}

		
	}

	public String exportZip(String daPath) {
		try {

			DAVRepositoryFactory.setup();
			SVNURL url = SVNURL.parseURIEncoded("http://svn.svnkit.com/repos/svnkit/trunk/doc");
			String userName = "foo";
			String userPassword = "bar";

			//Prepare filesystem directory (export destination).
			File exportDir = new File("/www/export.zip");
			if (exportDir.exists()) {
				SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.IO_ERROR, "Path ''{0}'' already exists",
						exportDir);
				throw new SVNException(err);
			}
//			exportDir.mkdirs();

			SVNRepository repository = SVNRepositoryFactory.create(url);

			ISVNAuthenticationManager authManager = SVNWCUtil.createDefaultAuthenticationManager(userName,
					userPassword);
			repository.setAuthenticationManager(authManager);

			SVNNodeKind nodeKind = repository.checkPath("", -1);
			if (nodeKind == SVNNodeKind.NONE) {
				SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.UNKNOWN, "No entry at URL ''{0}''", url);
				throw new SVNException(err);
			} else if (nodeKind == SVNNodeKind.FILE) {
				SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.UNKNOWN,
						"Entry at URL ''{0}'' is a file while directory was expected", url);
				throw new SVNException(err);
			}

			//Get latest repository revision. We will export repository contents at this very revision.
			long latestRevision = repository.getLatestRevision();

			ISVNReporterBaton reporterBaton = new ExportReporterBaton(latestRevision);

			ISVNEditor exportEditor = new ExportEditor(exportDir);

			/*
			 * Now ask SVNKit to perform generic 'update' operation using our reporter and editor.
			 * 
			 * We are passing:
			 * 
			 * - revision from which we would like to export
			 * - null as "target" name, to perform export from the URL SVNRepository was created for, 
			 *   not from some child directory.
			 * - reporterBaton
			 * - exportEditor.  
			 */
			repository.update(latestRevision, null, true, reporterBaton, exportEditor);

			return("Exported revision: " + latestRevision);
		} catch (SVNException sex) {
			return sex.getErrorMessage().getMessage();
		}
	}

	
}
